package com.fakesmoker.aidawnda.service.impl;

import cn.hutool.bloomfilter.BloomFilter;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fakesmoker.aidawnda.common.ErrorCode;
import com.fakesmoker.aidawnda.constant.CommonConstant;
import com.fakesmoker.aidawnda.exception.BusinessException;
import com.fakesmoker.aidawnda.exception.ThrowUtils;
import com.fakesmoker.aidawnda.mapper.TeamMapper;
import com.fakesmoker.aidawnda.model.dto.team.TeamQueryRequest;
import com.fakesmoker.aidawnda.model.dto.team.UserJoinTeamRequest;
import com.fakesmoker.aidawnda.model.dto.team.UserQuitTeamRequest;
import com.fakesmoker.aidawnda.model.entity.App;
import com.fakesmoker.aidawnda.model.entity.Team;
import com.fakesmoker.aidawnda.model.entity.User;
import com.fakesmoker.aidawnda.model.entity.UserTeam;
import com.fakesmoker.aidawnda.model.enums.TeamStatusEnum;
import com.fakesmoker.aidawnda.model.vo.TeamVO;
import com.fakesmoker.aidawnda.model.vo.UserVO;
import com.fakesmoker.aidawnda.service.TeamService;
import com.fakesmoker.aidawnda.service.UserService;
import com.fakesmoker.aidawnda.service.UserTeamService;
import com.fakesmoker.aidawnda.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.stream.Collectors;

/**
 * 用户回用户标签表服务实现
 *
 * @author lcy
 */
@Service
@Slf4j
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {

    @Resource
    private UserService userService;

    /**
     * 校验数据
     *
     * @param team
     * @param add  对创建的数据进行校验
     */
    @Override
    public void validTeam(Team team, boolean add) {
        ThrowUtils.throwIf(team == null, ErrorCode.PARAMS_ERROR);
        // todo 从对象中取值
        String name = team.getName();
        String description = team.getDescription();
        Integer maxNum = team.getMaxNum();
        Date expireTime = team.getExpireTime();
        Long userId = team.getUserId();
        Integer status = team.getStatus();
        String password = team.getPassword();

        // 创建数据时，参数不能为空
        if (add) {
            // todo 补充校验规则
            // 1. 队伍人数 > 1 且 <= 20
            ThrowUtils.throwIf(maxNum <= 1 || maxNum > 20, ErrorCode.PARAMS_ERROR, "队伍人数需大于1且小于等于20");
            // 2. 队伍标题 <= 20
            ThrowUtils.throwIf(StringUtils.length(name) > 20, ErrorCode.PARAMS_ERROR, "队伍标题长度需小于等于20");
            // 3. 描述 <= 512
            ThrowUtils.throwIf(StringUtils.length(description) > 512, ErrorCode.PARAMS_ERROR, "描述长度需小于等于512");

            // 4. status 是否公开（int）不传默认为 0（公开）
            TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
            ThrowUtils.throwIf(teamStatusEnum == null, ErrorCode.PARAMS_ERROR, "状态非法");

            // 5. 如果 status 是加密状态，一定要有密码，且密码 <= 32
            if (status == 2) {
                ThrowUtils.throwIf(StringUtils.isBlank(password) || StringUtils.length(password) > 32, ErrorCode.PARAMS_ERROR,
                        "加密状态下，密码不能为空且长度需小于等于32");
            }
            // 6. 超时时间 > 当前时间
            long currentTime = System.currentTimeMillis();
            long eT = expireTime.getTime();
            ThrowUtils.throwIf(eT <= currentTime, ErrorCode.PARAMS_ERROR, "已超时");
            // 7. 校验用户最多创建 5 个队伍 考虑收费
            QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userId", userId);
            long hasTeamNum = this.count(queryWrapper);
            if (hasTeamNum >= 5) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建 5 个队伍");
            }

        } else {
            // 修改数据时，有参数则校验

            if (StringUtils.isNotBlank(name)) {
                ThrowUtils.throwIf(name.length() > 80, ErrorCode.PARAMS_ERROR, "标题过长");
            }

            Long teamId = team.getId();
            if (teamId == null) {
                ThrowUtils.throwIf(true, ErrorCode.PARAMS_ERROR, "队伍ID不能为空");
            }
            TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
            ThrowUtils.throwIf(teamStatusEnum == null, ErrorCode.PARAMS_ERROR, "队伍状态不存在");

            // 如果修改为加密状态，密码不能为空且长度需小于等于32
            if (status.equals(TeamStatusEnum.ENCRYPT.getValue())) {
                ThrowUtils.throwIf(StringUtils.isBlank(password) || StringUtils.length(password) > 32, ErrorCode.PARAMS_ERROR, "加密状态下，密码不能为空且长度需小于等于32");
            }
        }
    }

    /**
     * 获取查询条件
     *
     * @param teamQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Team> getPageQueryWrapper(TeamQueryRequest teamQueryRequest, HttpServletRequest request) {
        QueryWrapper<Team> queryWrapper = getQueryWrapper(teamQueryRequest);

        Integer status = teamQueryRequest.getStatus();
        // 只有管理员才能查看加密还有非公开的房间
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        if (teamStatusEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "状态非法");
        }

        // 如果不是公开状态，则要校验用户是否为管理员
        if (teamStatusEnum != TeamStatusEnum.PUBLIC && userService.isAdmin(request)) {
            queryWrapper.eq("status", status);
        }
        // 非管理员，限制查询公开状态队伍
        queryWrapper.eq("status", TeamStatusEnum.PUBLIC.getValue());
        // 过期时间 > 本地时间 或者 过期时间为空
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        return queryWrapper;


    }


    @Resource
    private RedissonClient redissonClient;

    /**
     * 用户可以加入队伍
     *
     * @param userJoinTeamRequest
     * @param request
     * @return
     */
    @Transactional
    @Override
    public Boolean joinTeam(UserJoinTeamRequest userJoinTeamRequest, HttpServletRequest request) {
        Long teamId = userJoinTeamRequest.getTeamId();
        String password = userJoinTeamRequest.getPassword();
        User loginUser = userService.getLoginUser(request);
        Long userId = loginUser.getId();

        //用户最多加入5个队伍
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long count = userTeamService.count(queryWrapper);
        ThrowUtils.throwIf(count > 5, ErrorCode.PARAMS_ERROR, "用户最多加入5个队伍");

        //队伍必须存在，只能加入未满、未过期的队伍
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        long teamNum = userTeamService.count(queryWrapper);

        Team team = this.getById(teamId);
        ThrowUtils.throwIf(team == null, ErrorCode.PARAMS_ERROR, "队伍不存在");
        ThrowUtils.throwIf(team.getExpireTime().after(new Date()), ErrorCode.PARAMS_ERROR, "队伍不存在");
        ThrowUtils.throwIf(team.getMaxNum() >= teamNum, ErrorCode.PARAMS_ERROR, "人数已满");

        // 不能加入自己的队伍，不能重复加入已加入的队伍
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        queryWrapper.eq("teamId", teamId);
        long userTeamCount = userTeamService.count(queryWrapper);
        ThrowUtils.throwIf(userTeamCount > 0, ErrorCode.PARAMS_ERROR, "不能重复加入已加入的队伍");

        Long userId1 = team.getUserId();
        if (userId1.equals(userId)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "不能加入自己的队伍");
        }

        //禁止加入私有队伍
        Integer status = team.getStatus();
        if (TeamStatusEnum.PRIVATE.getValue().equals(status)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私有队伍");
        }

        // 如果加入的队伍是私密的，必须密码匹配才可以
        if (TeamStatusEnum.ENCRYPT.getValue().equals(status)) {
            ThrowUtils.throwIf(!password.equals(team.getPassword()), ErrorCode.PARAMS_ERROR, "密码错误");
        }

        //细粒度上锁
        // 生成唯一的锁键，确保不同用户和不同队伍有不同的锁
        String lockKey = "joinTeamLock:" + userId + ":" + teamId;
        RLock lock = redissonClient.getLock(lockKey);
        try {  // 尝试加锁，等待 10 秒，锁的持有时间为 30 秒
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                try {
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(userId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());

                    return userTeamService.save(userTeam);
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            } else {
                // 加锁失败，可根据实际情况处理，例如抛出异常或返回错误信息
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作过于频繁，请稍后重试");
            }
        } catch (InterruptedException e) {
            // 处理异常，例如记录日志，或者抛出异常
            Thread.currentThread().interrupt();
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "系统异常");
        }
    }

    @Override
    @Transactional
    public Boolean quitTeam(UserQuitTeamRequest userQuitTeamRequest, HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        Long userId = loginUser.getId();
        Long teamId = userQuitTeamRequest.getTeamId();

        //校验参数
        ThrowUtils.throwIf(teamId == null || teamId <= 0, ErrorCode.PARAMS_ERROR, "人数已满");

        //校验队伍是否存在
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        queryWrapper.eq("userId", userId);
        long teamNum = userTeamService.count(queryWrapper);
        ThrowUtils.throwIf(teamNum <= 0, ErrorCode.PARAMS_ERROR, "队伍不存在，不在队伍中");


        QueryWrapper<UserTeam> removeQueryWrapper = new QueryWrapper<>();
        removeQueryWrapper.eq("teamId", teamId);
        long countNum = userTeamService.count(removeQueryWrapper);
        if (countNum == 1) {
            //如果是最后一个用户退出，则删除队伍
            removeQueryWrapper.eq("userId", userId);
        } else {
            Team team = this.getById(teamId);
            //如果是队长退出队伍，权限转移给第二早加入的用户 ---先来后到
            if (team.getUserId().equals(userId)) {
                removeQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(removeQueryWrapper);
                ThrowUtils.throwIf(CollectionUtil.isEmpty(userTeamList) || userTeamList.size() <= 1, ErrorCode.PARAMS_ERROR, "队伍不存在，不在队伍中");
                UserTeam userTeam = userTeamList.get(1);
                Long newLeaderId = userTeam.getUserId();

                //更新队伍的队长
                Team newTeam = new Team();
                newTeam.setUserId(newLeaderId);
                newTeam.setId(teamId);
                boolean result = this.updateById(newTeam);

                ThrowUtils.throwIf(!result, ErrorCode.PARAMS_ERROR, "队伍不存在，不在队伍中");
            }
        }
        return userTeamService.remove(removeQueryWrapper);
    }

    /**
     * 获取查询条件
     *
     * @param teamQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Team> getQueryWrapper(TeamQueryRequest teamQueryRequest) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        if (teamQueryRequest == null) {
            return queryWrapper;
        }
        Long id = teamQueryRequest.getId();
        List<Long> idList = teamQueryRequest.getIdList();
        String searchText = teamQueryRequest.getSearchText();
        String name = teamQueryRequest.getName();
        String description = teamQueryRequest.getDescription();
        Integer maxNum = teamQueryRequest.getMaxNum();
        Long userId = teamQueryRequest.getUserId();

        // todo 补充需要的查询条件
        // 从多字段中搜索

        // 从请求参数中取出队伍名称等查询条件，如果存在则作为查询条件
        // 模糊查询队伍名称
        queryWrapper.like(Strings.isNotBlank(name), "name", name);
        // 模糊查询队伍描述
        queryWrapper.like(Strings.isNotBlank(description), "description", description);
        queryWrapper.like(Strings.isNotBlank(description), "searchText", searchText);


        queryWrapper.in(Strings.isNotBlank(description), "idList", idList);

        // 精确查询
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "maxNum", maxNum);

        return queryWrapper;
    }

    /**
     * 获取用户回用户标签表封装
     *
     * @param team
     * @param request
     * @return
     */
    @Override
    public TeamVO getTeamVO(Team team, HttpServletRequest request) {
        // 对象转封装类
        TeamVO teamVO = TeamVO.objToVo(team);
        // region 可选
        // 1. 关联查询用户信息
        Long userId = team.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        teamVO.setUser(userVO);
        // endregion

        return teamVO;
    }

    /**
     * 分页获取用户回用户标签表封装
     *
     * @param teamPage
     * @param request
     * @return
     */
    @Override
    public Page<TeamVO> getTeamVOPage(Page<Team> teamPage, HttpServletRequest request) {
        List<Team> teamList = teamPage.getRecords();
        Page<TeamVO> teamVOPage = new Page<>(teamPage.getCurrent(), teamPage.getSize(), teamPage.getTotal());
        if (CollUtil.isEmpty(teamList)) {
            return teamVOPage;
        }
        // 对象列表 => 封装对象列表
        List<TeamVO> teamVOList = teamList.stream().map(team -> {
            return TeamVO.objToVo(team);
        }).collect(Collectors.toList());


        // 1. 关联查询创建者用户信息
        Set<Long> userIdSet = teamList.stream().map(Team::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 填充信息
        teamVOList.forEach(teamVO -> {
            Long userId = teamVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
                // 脱敏
                UserVO userVO = userService.getUserVO(user);
                teamVO.setUser(userVO);
            }
        });
        // 2. 查询队伍其他成员信息
        // 2. 查询队伍其他成员信息
        Set<Long> teamIds = teamList.stream().map(Team::getId).collect(Collectors.toSet());

        //根据teamId分组查询所有的用户关系表中信息包括userId 处理成Map
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.in("teamId", teamIds);
        List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
        Map<Long, List<UserTeam>> teamUserTeamMap = userTeamList.stream()
                .collect(Collectors.groupingBy(UserTeam::getTeamId));

        // 3. 填充队伍所有成员信息 遍历分页对象中 需要处理的返回的对象列表
        teamVOList.forEach(teamVO -> {
            Long teamId = teamVO.getId();
            List<UserTeam> userTeams = teamUserTeamMap.getOrDefault(teamId, Collections.emptyList());

            //  处理成队伍的成员id集合
            Set<Long> memberIds = userTeams.stream().map(UserTeam::getUserId).collect(Collectors.toSet());
            if (!CollUtil.isEmpty(memberIds)) {
                // 根据所有用户id集合查询对应的用户信息集合
                List<User> users = userService.listByIds(memberIds);
                // 脱敏
                List<UserVO> userVOList = userService.getUserVO(users);
                // 填充队伍 所有成员信息
                teamVO.setUsers(userVOList);

            }
        });

        teamVOPage.setRecords(teamVOList);
        return teamVOPage;
    }

    @Resource
    private UserTeamService userTeamService;

    @Override
    @Transactional
    public Long addTeam(Team team, User loginUser) {
        Long userId = loginUser.getId();

        team.setId(null);
        team.setUserId(userId);
        //1. 插入队伍信息到队伍表
        boolean save = this.save(team);
        Long teamId = team.getId();

        if (!save || teamId == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "创建队伍失败");
        }
        //2. 插入用户 => 队伍关系到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());

        boolean save1 = userTeamService.save(userTeam);
        if (!save1) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "加入队伍失败");
        }

        return teamId;
    }

}
